﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


// DebugMode.h
// 目标：用来动态设置调试位,以动态控制程序的行为。可在“调试位标志"命令中动态设置调试位。

// 使用方法：
//     1. 在.cpp文件中，调用CREATE_DEBUG_MODE注册调试标识位。如
//        CREATE_DEBUG_MODE(DebugRegeneration, L"打印关联更新信息", DMGT_DEBUG_INFO, L"周智华"); 

//          如果跨DLL使用，则用CREATE_DEBUG_MODE_EXPORT注册调试标识位。如
//          CREATE_DEBUG_MODE_EXPORT(SetNoUseOCtree, L"关闭八叉树",gcmp::DMGT_CAPTURE_PICK,L"肖云");

//     2. 如果在同一DLL其它文件中使用，需用EXPOSE_DEBUG_MODE(DebugRegeneration)声明调试标识位。如
//        EXPOSE_DEBUG_MODE(DebugRegeneration); 

//        如果跨DLL文件使用，则需用EXPOSE_DEBUG_MODE_EXPORT声明调试标识位。如
//          EXPOSE_DEBUG_MODE_EXPORT(SetNoUseOCtree, GMPICK_EXPORT);

//       3. 使用DEBUG_MODE测试标识位是否开启。
//        if (DEBUG_MODE(DebugRegeneration)) 
//            doSomething...    
//
//        也可以使用DEBUG_MODE_SLOW，使用DEBUG_MODE_SLOW无需第二步声明,但效率稍差，不推荐。

// 目标：整理DebugMode。   
//     1. DebugMode增加分组，界面上可以按组显示。
//     2. DebugMode增加联系人选项。  
//     3. 可以显示所有启用的DebugMode。
//     4. 默认所有DebugMode都不启用。
// 作者：gaoyu。
// 日期：2016年05月23日。
// 类型：修改。


#pragma once

#include "GcmpDevService.h"
#include <functional>
#include <map>
#include <unordered_map>
#include "OwnerPtr.h"

#pragma warning( push )
#pragma warning( disable : 4251 )

#define CREATE_DEBUG_MODE(MODE_NAME, DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE) \
    EXPOSE_DEBUG_MODE(MODE_NAME) \
    gcmp::DebugMode gcmp::DEBUG_MODE_##MODE_NAME(WSTRING_OF(MODE_NAME), DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE)

#define EXPOSE_DEBUG_MODE(MODE_NAME) \
    namespace gcmp { extern DebugMode DEBUG_MODE_##MODE_NAME; };

#define DEBUG_MODE(MODE_NAME) \
    gcmp::DEBUG_MODE_##MODE_NAME.IsSet()

#define CREATE_DEBUG_MODE_EXPORT(MODE_NAME, DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE) \
    EXPOSE_DEBUG_MODE_EXPORT(MODE_NAME, DLL_EXPORT) \
    gcmp::DebugMode gcmp::DEBUG_MODE_##MODE_NAME(WSTRING_OF(MODE_NAME), DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE)

#define CREATE_DEBUG_MODE_EXPORT_WITH_CALLBACK(MODE_NAME, DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE, CALLBACK) \
    EXPOSE_DEBUG_MODE_EXPORT(MODE_NAME, DLL_EXPORT) \
    gcmp::DebugMode gcmp::DEBUG_MODE_##MODE_NAME(WSTRING_OF(MODE_NAME), DESCRIPTION, GROUP_ENUM, OWNER, REVIEW_TIME, USAGE, CALLBACK)

#define EXPOSE_DEBUG_MODE_EXPORT(MODE_NAME, DLL_EXPORT_NAME) \
    namespace gcmp { extern DLL_EXPORT_NAME DebugMode DEBUG_MODE_##MODE_NAME; };

// 无需包含头文件看到CREATE_DEBUG_MODE和EXPOSE_DEBUG_MODE就可以使用
// 性能稍差
#define DEBUG_MODE_SLOW(MODE_NAME) \
    gcmp::DebugModeManager::Singleton().IsDebugModeSet(WSTRING_OF(MODE_NAME))

#define DEBUG_MODE_VARIABLE(MODE_NAME) gcmp::DEBUG_MODE_##MODE_NAME

namespace gcmp
{
    class GCMP_DEV_SERVICE_EXPORT DebugModeState
    {
        CANNOT_COPY_OR_ASSIGN(DebugModeState);
    private:
        DebugModeState(void);
    public:
        ~DebugModeState(void);

    public:
        static DebugModeState& Singleton(void);
        bool GetDebugModeState(const std::wstring& id);

    private:
        void CacheDebugModesFromConfigFile(void);

    private:
        std::unordered_map<std::wstring, bool> m_debugModesFromConfigFile;
    };

    class DebugModeGroup
    {
        CANNOT_COPY_OR_ASSIGN(DebugModeGroup);
    private:
        DebugModeGroup(void);
    public:
        ~DebugModeGroup(void);

    public:
        //CREATE_DEBUG_MODE时需要输入的分组信息的种类
        // ！！注意！！ 若要添加新项，请同步添加对应字符串至s_debugModeGroupTypeNames
        enum EnDebugModeGroupType
        {
            DMGT_UI_AUTO_TEST = 0,
            DMGT_PERFORMANCE_TIMER,
            DMGT_MEMORY_MANAGEMENT,
            DMGT_DATA_VALIDATION,
            DMGT_DEBUG_INFO,

            DMGT_DATA_STORAGE,
            DMGT_FILE_UPGRADE,
            DMGT_DOCUMENT_ELEMENT,

            DMGT_GEOMETRY_LIBRARY,
            DMGT_GREP_AND_PICKSNAP,
            DMGT_REGENERATION,
            DMGT_FAMILY_SERVICE,
            DMGT_VIEW,
            DMGT_RENDERING,
            DMGT_API,
            DMGT_FORCE_ENABLE_DISABLE,

            DMGT_DATA_EXCHANGE,
            DMGT_CONSTRAINT,
            DMGT_SKETCH,
            DMGT_INSTANCE_JOIN,
            DMGT_INSTANCE_CREATION,

            DMGT_PRODUCT_SCAFFOLD,

            DMGT_LINK,
            DGMT_PERFORMANCE_TEST,

            DMGT_ELEMENT_COLLABORATION,
            DMGT_COLLABORATION,
            
            // 请在此项以上添加新的group种类
            DMGT_OTHERS,
            DMGT_NUM_OF_TYPES
        };

    private:
        std::wstring s_debugModeGroupTypeNames[DMGT_NUM_OF_TYPES];//EnDebugModeGroupType枚举型和输出字符串的对应表

    private:
        void InitializeDMGTNames(void);

    public:
        static DebugModeGroup& GetDebugModeGroup(void);
        static std::wstring GetDebugModeGroupTypeNames(EnDebugModeGroupType group);
    };

    class GCMP_DEV_SERVICE_EXPORT DebugMode
    {
        CANNOT_COPY_OR_ASSIGN(DebugMode);
    public:
        DebugMode(
            const std::wstring& id,
            const std::wstring& description,
            DebugModeGroup::EnDebugModeGroupType group,
            const std::wstring& linkman,
            const std::wstring& lastReviewTime = L"请联系人审核",    // 最后审核时间。TODO: 全部DebugMode加上改字段之后改为不可缺省的参数
            const std::wstring& usage = L"请添加使用方法说明"        // 使用方法说明。TODO: 全部DebugMode加上改字段之后改为不可缺省的参数
        );
        DebugMode(
            const std::wstring& id,
            const std::wstring& description,
            DebugModeGroup::EnDebugModeGroupType group,            
            const std::wstring& linkman,
            std::function<void(bool)> valueChangedCallBackFunc,          //DebugMode 变化时的回调
            const std::wstring& lastReviewTime = L"请联系人审核",    // 最后审核时间。TODO: 全部DebugMode加上改字段之后改为不可缺省的参数
            const std::wstring& usage = L"请添加使用方法说明"       // 使用方法说明。TODO: 全部DebugMode加上改字段之后改为不可缺省的参数            
        );
        ~DebugMode(void);

        FORCE_INLINE const std::wstring& GetDescription() const { return m_description; }
        FORCE_INLINE void SetDescription(const std::wstring& val) { m_description = val; }
        FORCE_INLINE bool IsSet() const { return m_isSet; }
        FORCE_INLINE void Set(bool val) 
        { 
            m_isSet = val; 
            if (m_valueChangedCallBackFunc)
            {
                m_valueChangedCallBackFunc(m_isSet);
            }
        }
        FORCE_INLINE std::wstring GetId() const { return m_id; }
        FORCE_INLINE std::wstring GetGroup() const { return m_group; }
        FORCE_INLINE std::wstring GetLinkMan() const { return m_linkman; }
        FORCE_INLINE void SetId(std::wstring val) { m_id = val; }
        FORCE_INLINE std::wstring GetLastReviewTime() const { return m_lastReviewTime; }
        FORCE_INLINE std::wstring GetUsage() const { return m_usage; }
    private:
        std::wstring m_id;
        std::wstring m_description;
        bool m_isSet;
        std::wstring m_group;
        std::wstring m_linkman;
        std::wstring m_lastReviewTime;
        std::wstring m_usage;
        friend class DebugModeManager;
        std::function<void(bool)> m_valueChangedCallBackFunc = nullptr;
    };

    class GCMP_DEV_SERVICE_EXPORT DebugModeManager
    {
        CANNOT_COPY_OR_ASSIGN(DebugModeManager);
    public:
        static DebugModeManager& Singleton();
        bool IsDebugModeSet(const std::wstring& id) const;
        void SetDebugMode(const std::wstring& id, bool val);
        bool IsDebugModeRegister(const std::wstring &id) const;
        bool RegisterDebugMode(DebugMode* pDebugMode);
        bool UnRegisterDebugMode(DebugMode* pDebugMode);
        void RegisterDanymicDebugMode(const std::wstring& id, const std::wstring& description, DebugModeGroup::EnDebugModeGroupType group, const std::wstring& linkman);
        void forEach(const std::function<void(DebugMode& debugMode)>& callback);
        std::unordered_map<std::wstring, DebugMode*>& GetDebugModeMap(){return m_debugModeMap;}
        const DebugMode* GetDebugMode(const std::wstring& id) const;

        // 缓存启动时从命令行参数读入的DebugMode
        void CacheDebugModeFromCommandLineArguments(const std::wstring& id, bool enable);
        // 设置启动时从命令行参数读入的DebugMode，必须在所有DebugModes创建并注册之后。DebugMode初始值优先级从高到低：1）命令行；2）构造时初始值
        void SetDebugModesFromCommandLineArguments();
        
        /// \brief 判断DebugMode是否在命令行参数缓存中
        /// \note 该缓存中的DebugMode是指模块未加载时，尚未注册的部分DebugMode；已经注册的DebugMode会第一时间设置状态，不会加到该缓存中。
        ///
        /// \param id DebugMode的id（名字）
        /// \return true：在缓存中，且状态为true。false：不在缓存中；或在缓存中，预设状态为false
        bool GetDebugModeStatusFromCachedCommandLineArguments(const std::wstring& id) const;

    private:
        DebugModeManager();
    private:
        std::unordered_map<std::wstring, DebugMode*> m_debugModeMap;
        std::vector<OwnerPtr<DebugMode>> m_dynamicModes;  // created by new danymically
        std::unordered_map<std::wstring, bool> m_debugModesFromCommandLineArguments;
    };
}

#pragma warning( pop )
